/**
 * @(#)HtmlParser.java, 2010-5-12. 
 * 
 * Copyright 2010 Youdao, Inc. All rights reserved.
 * YOUDAO PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.rsstuan.parser;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.filters.StringFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.nodes.TextNode;
import org.htmlparser.tags.ImageTag;
import org.htmlparser.tags.TableColumn;
import org.htmlparser.tags.TableRow;
import org.htmlparser.tags.TableTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

import com.google.appengine.api.images.Image;
import com.rsstuan.storage.data.TuanGouItemData;
import com.rsstuan.utils.ItemUtils;
import com.rsstuan.utils.StringUtils;
import com.rsstuan.utils.UrlUtils;

/**
 *
 * @author guosq
 *
 */
public class HtmlParser {

	private final Logger LOG = Logger.getLogger(HtmlParser.class.getName());
	private final static String DEFAULT_ENCODING = "utf-8";

	private Parser parser;
	
	private String url;
	
	private String html;
	
	private Node titleNode;
	
	private ImageTag imageNode;
	
	public HtmlParser(String html, String url) {
		String charSet = detectEncoding(html, url);
		this.html = html;
		this.url = url;
		this.parser = Parser.createParser(html, charSet);
		
	}
    public NodeList parse(String html) throws ParserException {
       NodeList root = parser.parse(null);
       return root;
       
    }
    private String detectEncoding(String html, String url) {
    	String charSet = DEFAULT_ENCODING;
    	if (url.contains("55tuan"))
    		charSet = "gbk";
    	
    	return charSet;
    }
    
    public TuanGouItemData parseToTuanGouItem()  {
    	
    	try {
    		TuanGouItemData item = new TuanGouItemData();
    		NodeList root = parser.parse(null);
    		
    		String content = getAllText(root);
    		
    		this.titleNode = getTitle(item);
    		this.imageNode = getImage(item);
			if (imageNode != null)
			    item.setImage(adjustInnerLink(imageNode.getImageURL()));
			analyzePrices(item);
			
			//System.out.println(content);
			item.setAddress(StringUtils.cut(ItemUtils.getAddress(content), 200));
			item.setOrderNum(ItemUtils.getOrderNum(content));
			item.setLeftTime(ItemUtils.getLeftTime(content));
			item.setEndTime(ItemUtils.getEndTime(content));
			
			return item;
    	}
    	catch (Exception e) {
    		LOG.log(Level.WARNING, "parse failed", e);
    		return null;
    	}
    }
    
    
    private Node getTitle(TuanGouItemData item) {
        final int MIN_H1_TITLE_LENGTH = 20;
       
    	
    	try {
    	    // extract h1
    		parser.reset();
    		NodeFilter filter = new TagNameFilter("H1");
			NodeList list = parser.extractAllNodesThatMatch(filter);
			for (int i = 0; i < list.size(); i ++ ) {
			    String title = list.elementAt(i).toPlainTextString();
			    if (title.length() > MIN_H1_TITLE_LENGTH) {
			        item.setTitle(title);
			        return list.elementAt(i);
			    }
			    
			}
			
			//extract h2
			parser.reset();
            filter = new TagNameFilter("H2");
            list = parser.extractAllNodesThatMatch(filter);
            for (int i = 0; i < list.size(); i ++ ) {
                String title = list.elementAt(i).toPlainTextString();
                if (title.length() > MIN_H1_TITLE_LENGTH) {
                    item.setTitle(title);
                    return list.elementAt(i);
                }
                
            }
            //special for aibang
            if (UrlUtils.getUrlDomain(url).contains("aibang")) {
                parser.reset();
                filter = new NodeClassFilter(TableTag.class);
                list = parser.extractAllNodesThatMatch(filter);
                for (int i = 0; i < list.size(); i ++) {
                    TableTag table = (TableTag)list.elementAt(i);
                    if ("at_jrat".equals(table.getAttribute("class"))) {
                        item.setTitle(table.toPlainTextString());
                        return table;
                    }
                }
            }
            //extract key words
            parser.reset();
            if (UrlUtils.getUrlDomain(url).contains("groupon")) {
                filter = new StringFilter("今日团购");
            } else {
                filter = new StringFilter("今日团购(:|：)");
            }
            list = parser.extractAllNodesThatMatch(filter);
            
            for (int i = 0; i < list.size(); i ++ ) {
                
                Node n = list.elementAt(i);
                StringBuilder sb = new StringBuilder(); 
                sb.append(n.toPlainTextString());
                while (sb.length() < MIN_H1_TITLE_LENGTH) {
                    if (n.getNextSibling() != null) {
                        n = n.getNextSibling();
                    } else if (n.getParent().getNextSibling() != null) {
                        n = n.getParent().getNextSibling();
                    } else {
                        n = n.getParent().getParent().getNextSibling();
                    }
                    
                    if (n == null)
                        break;
                    sb.append(n.toPlainTextString());
                }
                if (sb.length() > MIN_H1_TITLE_LENGTH) {
                    item.setTitle(sb.toString());
                    return list.elementAt(i);
                }    
            }
            
            
            return null;
		} catch (ParserException e) {
		    LOG.log(Level.WARNING, "Can not parse title", e);
			return null;
		}
    }
    
    private ImageTag getImage(TuanGouItemData item) {
    	NodeFilter filter = new NodeClassFilter(ImageTag.class);
    	//List<String> images = new ArrayList<String>();
    	try {
    		parser.reset();
    		NodeList list = parser.extractAllNodesThatMatch(filter);
    		ImageTag img = null;
    		for (int i = 0; i < list.size(); i ++) {
    			ImageTag tag = (ImageTag) list.elementAt(i);
    			String imgUrl = tag.getImageURL();
    			//过滤所有图片
    			if (filterImage(imgUrl)) { 
    			    if (img == null) {
    			        img = tag;
    			    } else if (titleNode != null) {
    			        //根据与标题的距离筛选图片
    			        int space = tag.getStartPosition() - titleNode.getEndPosition(); 
    			        int curspace = img.getStartPosition() - titleNode.getEndPosition();
    			        if ( curspace < 0 ) {
    			            img = tag;
    			        } else if (space > 0 && space < curspace) {
    			            img = tag;
    			        } else if (!StringUtils.isBlank(img.getAttribute("alt"))) {
    			            int len = item.getTitle().length();
    			            int start;
    			            if (len < 20) 
    			                start = 5;
    			            else
    			                start = len - 15;
    			            String subTitle = item.getTitle().substring(start, len - 5);
    			            String desc = tag.getAttribute("alt");
    			            String imgDesc = img.getAttribute("alt");
    			            if (desc != null && desc.contains(subTitle)) {
    			                if (imgDesc == null || !imgDesc.contains(subTitle)) {
    			                    img = tag;
    			                }
    			            }
    			        }
    			    } else {
    			        //TODO
    			        //加入其他筛选规则
    			    } 
    			}
    		}
    			
    		return img;
    	} catch (ParserException e) {
    		LOG.log(Level.WARNING, "getImage failed", e);
    		return null;
    	}
    }
    
    private void analyzePrices(TuanGouItemData item) {
        Double origPrice = null;
        Double price = null;
        if (item.getTitle() != null) {
            origPrice = ItemUtils.getOrigPrice(item.getTitle());
            price = ItemUtils.getPrice(item.getTitle());
        }
        if (item.getDescription() != null && origPrice == null )
            origPrice = ItemUtils.getOrigPrice(item.getDescription());
        if (item.getDescription() != null && price == null)
            origPrice = ItemUtils.getPrice(item.getDescription());
        item.setOrigPrice(origPrice);
        item.setPrice(price);
        
        if (item.getPrice() == null || item.getOrigPrice() == null) {
            extractPricesFromTable(item);
        }
        if (item.getPrice() == null || item.getOrigPrice() == null) {
            extractPricesFromList(item);
        }
        
        
        
    }
    
    private String adjustInnerLink(String link) {
    	String retLink = link;
    	String domain = UrlUtils.getUrlDomain(url);
    	if (!link.startsWith("http")) {
    		if (link.startsWith("/")) {
    			retLink = domain + link;
    		} else {
    			retLink = domain + "/" + link;
    		}
    	} else {
    		retLink = link;
    	}
    	return retLink;
    }
    
    public String getAllText(NodeList root) {
        StringBuilder sb = new StringBuilder();
        if (root == null)
            return "";
        for (int i = 0; i < root.size(); i ++) {
            Node n = root.elementAt(i);
            if (n instanceof TextNode) {
                sb.append(n.toPlainTextString());
            }
            else {
                sb.append(getAllText(n.getChildren()));
            }
        }
        return sb.toString();
    }
    
    private void extractPricesFromTable(TuanGouItemData item) {
        Double price = item.getPrice();
        Double origPrice = item.getOrigPrice();
        Double savePrice = null;
        Double discount = null;
        NodeFilter filter = new NodeClassFilter(TableTag.class);
        try {
            parser.reset();
            NodeList list = parser.extractAllNodesThatMatch(filter);
            for (int i = 0; i < list.size(); i++) {
                TableTag table = (TableTag) list.elementAt(i);
                TableRow[] rows = table.getRows();
                if (rows != null && rows.length > 1) {
                    int oIndex = -1; // 原价的列
                    int zIndex = -1; // 折扣的列
                    int sIndex = -1; // 节省的列
                    for (int c = 0; c < rows[0].getChildCount(); c++) {
                        String text = rows[0].getChild(c)
                                .toPlainTextString();
                        if (text.contains("原价")) {
                            oIndex = c;
                        } else if (text.contains("折扣")) {
                            zIndex = c;
                        } else if (text.contains("节省")) {
                            sIndex = c;
                        }
                    }
                    if (oIndex >= 0) {
                        origPrice = ItemUtils.extractDigits(rows[1]
                                .getChild(oIndex).toPlainTextString());
                    }
                    if (sIndex >= 0) {
                        savePrice = ItemUtils.extractDigits(rows[1]
                                .getChild(sIndex).toPlainTextString());
                    }

                    if (zIndex >= 0) {
                       discount = ItemUtils.extractDigits(rows[1]
                                .getChild(zIndex).toPlainTextString());
                    }   
                    if (oIndex >= 0 || sIndex >=0 || zIndex >=0) {
                    	LOG.info("oIndex=" + oIndex + ",sIndex=" + sIndex + ", zIndex=" + zIndex);
                    	break;  	
                    }
                }
            }
            if (origPrice == null && price != null) {
            	if (savePrice != null) {
            		origPrice = price + savePrice;
            	} else if (discount != null) {
            		origPrice = price * 10 / discount;
            	}
            } else if (price == null && origPrice != null) {
            	if (savePrice != null) {
            		price = origPrice - savePrice;
            	} else if (discount != null) {
            		price = origPrice * discount / 10;
            	}
            }

            if (price != null) {
                item.setPrice(price);
            }
            if (origPrice != null) {
                item.setOrigPrice(origPrice);
            }
        } catch (ParserException e) {
            LOG.log(Level.WARNING, "fall to analyzePrices", e);
        }
    }

    private void extractPricesFromList(TuanGouItemData item) {
        LOG.info("enter extractPricesFromList");
        Double price = null;
        Double origPrice = null;
        Double savePrice = null;
        Double discount = null;
        NodeFilter lf = new TagNameFilter("LI");
        try {
            parser.reset();
            NodeList list = parser.extractAllNodesThatMatch(lf);
            for (int i = 0; i < list.size(); i ++ ) {
                String text = list.elementAt(i).toPlainTextString();
                if (text.contains("原价")) {
                    Node n = list.elementAt(i);
                    while (n != null) {
                        Double d = ItemUtils.extractDigits(n.toPlainTextString());
                        if (d != null && origPrice == null) {
                            origPrice = d;
                            break;
                        }
                        n = n.getNextSibling();
                    }
                }
                
                if (text.contains("节省")) {
                    Node n = list.elementAt(i);
                    while (n != null) {
                        Double d = ItemUtils.extractDigits(n.toPlainTextString());
                        if (d != null && savePrice == null) {
                            savePrice = d;
                            break;
                        }
                        n = n.getNextSibling();
                    }
                }
                
                if (text.contains("折扣")) {
                    Node n = list.elementAt(i);
                    while (n != null) {
                        Double d = ItemUtils.extractDigits(n.toPlainTextString());
                        if (d != null && discount == null) {
                            discount = d;
                            break;
                        }
                        n = n.getNextSibling();
                    }
                }
            }
            
            if (item.getOrigPrice() == null) 
                item.setOrigPrice(origPrice);
            if (item.getPrice() == null && item.getOrigPrice() != null) {
                if (savePrice != null) {
                    item.setPrice(item.getOrigPrice() - savePrice);
                } else if (discount != null) {
                    item.setPrice(item.getOrigPrice() * discount / 10);
                }
            } else if (item.getOrigPrice() == null && item.getPrice() != null) {
            	if (savePrice != null) {
            		item.setOrigPrice(item.getPrice() + savePrice);
            	} else if (discount != null) {
            		item.setOrigPrice(item.getPrice() * 10 / discount);
            	}
            }
            
        } catch (Exception e) {
            LOG.log(Level.WARNING, "extractPricesFromList failed", e);
        }
    }
    
    private boolean filterImage(String imageUrl) {
        String domain = UrlUtils.getUrlDomain(url).toLowerCase();
        if (domain.contains("aibang")) {
            return imageUrl.toLowerCase().contains("img6");
        }
        
        if (domain.contains("groupon")) {
            return imageUrl.toLowerCase().contains("uploadimages");
        }
        
        if (imageUrl.toLowerCase().contains("jpg")) {
            return true;
        }
        
        return false;
    }

}
